package classfile

import (
	"fmt"
)

/*
	ClassFile {
	    u4             magic;
	    u2             minor_version;
	    u2             major_version;
	    u2             constant_pool_count;
	    cp_info        constant_pool[constant_pool_count-1];
	    u2             access_flags;
	    u2             this_class;
	    u2             super_class;
	    u2             interfaces_count;
	    u2             interfaces[interfaces_count];
	    u2             fields_count;
	    field_info     fields[fields_count];
	    u2             methods_count;
	    method_info    methods[methods_count];
	    u2             attributes_count;
	    attribute_info attributes[attributes_count];
	}
*/

type ClassFile struct {
	// 魔数
	magic uint32
	// 小版本 大版本
	minorVersion uint16
	majorVersion uint16
	// 常量池
	constantPool ConstantPool
	// 修饰符
	accessFlags uint16
	thisClass   uint16
	superClass  uint16
	interfaces  []uint16
	fields      []*MemberInfo
	methods     []*MemberInfo
	attributes  []AttributeInfo
}

func (self *ClassFile) read(cr *ClassReader) {
	self.readMagic(cr)
	self.readVersion(cr)
	self.readConstantPool(cr)
	self.readAccessFlags(cr)
	self.readClassName(cr)
	self.readSuperClassName(cr)
	self.readInterfaces(cr)
	self.readFields(cr)
	self.readMethods(cr)
	self.readAttributes(cr)

}
func Parse(classData []byte) (cf *ClassFile, err error) {
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok := r.(error)
			if ok {
				fmt.Println(err)
				err = fmt.Errorf("9%v\n", r)
			}
		}
	}()
	cr := &ClassReader{classData, 0}
	cf = &ClassFile{}
	cf.read(cr)
	return
}

func (self *ClassFile) readMagic(cr *ClassReader) {
	self.magic = cr.ReadUInt32()
	if self.magic != 0xCAFEBABE {
		panic("不是有效的java文件")
	}
}

func (self *ClassFile) readVersion(cr *ClassReader) {
	self.readMinorVersion(cr)
	self.readMajorVersion(cr)
	fmt.Println("class file minorVersion", self.minorVersion, "majorVersion", self.majorVersion)

}

func (self *ClassFile) readMajorVersion(cr *ClassReader) {
	self.minorVersion = cr.ReadUInt16()
}

func (self *ClassFile) readMinorVersion(cr *ClassReader) {
	self.minorVersion = cr.ReadUInt16()
}

func (self *ClassFile) readConstantPool(cr *ClassReader) {
	self.constantPool = ReadConstantPool(cr)
}

func (self *ClassFile) readAccessFlags(cr *ClassReader) {
	self.accessFlags = cr.ReadUInt16()
}

func (self *ClassFile) readFields(cr *ClassReader) {
	memberCount := cr.ReadUInt16()
	self.fields = make([]*MemberInfo, memberCount)
	for i := 0; i < int(memberCount); i++ {
		self.fields[i] = ReadMemberInfo(cr, self.constantPool)
	}
}

func (self *ClassFile) readMethods(cr *ClassReader) {
	memberCount := cr.ReadUInt16()
	self.methods = make([]*MemberInfo, memberCount)
	for i := 0; i < int(memberCount); i++ {
		self.methods[i] = ReadMemberInfo(cr, self.constantPool)
		c := self.constantPool.GetConstant(self.methods[i].nameIndex)
		if data, ok := c.(*Constant_String); ok {
			println(data.value.Value)
		}
		if data, ok := c.(*Constant_Utf8); ok {
			println(data.Value)
		}
	}
}

func (self *ClassFile) readClassName(cr *ClassReader) {
	self.thisClass = cr.ReadUInt16()
}

func (self *ClassFile) readSuperClassName(cr *ClassReader) {
	self.superClass = cr.ReadUInt16()
}
func (self *ClassFile) readInterfaces(cr *ClassReader) {
	size := cr.ReadUInt16()
	if size > 0 {
		self.interfaces = make([]uint16, size)
		for i := 0; i < int(size); i++ {
			self.interfaces[i] = cr.ReadUInt16()
		}
	}
}

func (self *ClassFile) readAttributes(cr *ClassReader) {
	self.attributes = make([]AttributeInfo, cr.ReadUInt16())
	for i := 0; i < len(self.attributes); i++ {
		self.attributes[i] = NewAttributeInfo(cr, self.constantPool)
	}
}

func (self ClassFile) AccessFlags() uint16 {
	return self.accessFlags
}

func (self ClassFile) ClassName() string {
	return self.constantPool.getClassName(self.thisClass)
}

func (cf *ClassFile) SourceFileAttribute() *SourceFileAttr {
	for _, attrInfo := range cf.attributes {
		switch attrInfo.(type) {
		case *SourceFileAttr:
			return attrInfo.(*SourceFileAttr)
		}
	}
	return nil
}

func (self *ClassFile) GetSourceFile(cf *ClassFile) string {
	if sfAttr := cf.SourceFileAttribute(); sfAttr != nil {
		return sfAttr.GetSourceFile()
	}
	return "Unknown"
}

func (self *ClassFile) SuperClassName() string {
	if self.superClass > 0 {
		return self.constantPool.getClassName(self.superClass)
	}
	return ""
}

func (self *ClassFile) InterfaceNames() []string {
	var vars = make([]string, 0, 0)
	if self.interfaces != nil && len(self.interfaces) > 0 {
		for _, inter := range self.interfaces {
			vars = append(vars, self.constantPool.getClassName(inter))
		}
	}
	return vars
}

func (self *ClassFile) GetConstantPool() ConstantPool {
	return self.constantPool
}

func (self *ClassFile) Fields() []*MemberInfo {
	return self.fields
}

func (self *ClassFile) Methods() []*MemberInfo {
	return self.methods
}
